How to manipulate a gene list

The golden rule before we start Always keep a backup of the output from the pipeline, never work on the original data!

There are various tasks you might wish to perform on a gene list

  • Search for our favourite gene
  • Sort / Rank according to statistic or p-value
  • Filter to obtain genes with particular cut-off and fold-change
  • Re-order / re-arrange columns

Our recommended tools to perform such operations would be a programming language such as R / Python so that the operations can be scripted and automated. As a compromise, our Galaxy server provides various operations through an intuitive interface and allows the user to build a workflow to a chain of simple operations.

There is a special reason why you shouldn’t trust Excel as your many means of interrogating the results. Sometimes it can be unhelpful and convert gene identifers into dates. No, really!

We will only scratch the surface of what is possible with Galaxy. If you are new to the software, you can check out our course:- http://galaxycam.github.io/

Different online tools or GUIs will take different types of input, but will probably involve some combination of the operations mentioned above. Therefore, we will introduce some tools in Galaxy that should allow you to manipulate your data into the required form. Later, we will give examples of workflows for particular gene set enrichment / pathways analysis tools.

In the first generic example, we will show some common manipulations on a gene list using Galaxy:-

Common tasks in Galaxy

Upload the gene list in csv form

The gene list .csv file can be uploaded into Galaxy. In this example we want to choose the file with no p-value cut-offs applied

Convert to tabular

Before we can go further, Galaxy needs to convert the data you just uploaded into tabular form by replacing commas with a tab. This can be done using the Text Manipulation -> Convert delimiters to TAB option. Make sure that Commas is selected from the drop-down.

Filter

We can apply filtering using the menu option Filter and Sort -> Filter data on any column using simple expressions. Here we use the condition c7 <0.01 and c3 > 1.5 to require that Column 7 (adjusted p-value) is less than 0.05 and Column 3 (log fold-change) is greater than 1.5.

You could also use this menu to filter on genes on a particular chromosome or within a certain range of start and end positions

Selecting columns

If you find the number of columns in the file a bit unwieldy, you can also use the Text Manipulation -> Cut columns from a table to remove some columns, or re-order the existing ones:-

Sorting by a column

We can also sort the table by values in a particular column. e.g. the log\(_2\) fold change which is found in column number 3.

  • can also sort by multiple columns
    • e.g. Chromosome, and then start

R workflow

For those keen on R, equivalent operations can be performed with the dplyr package. See our intermediate R course for details.

library(dplyr)
deTable <- read.csv("t47d_Treatment_DEA_Prog-vs-Control_all.csv")
filteredTable <- filter(deTable, padj < 0.05, log2FoldChange > 1.5)
sortedTable <- arrange(filteredTable, log2FoldChange)
reducedTable <- select(filteredTable, X,baseMean,log2FoldChange)

Gene-Ontologies and Pathways

In the early days of microarray analysis, people were happy if they got a handful of differentially-expressed genes that they could validate or follow-up. However, with later technologies (and depending on the experimental setup) we might have thousands of statistically-significant results, which no-one has the time to follow-up. Also, we might be interested in pathways / mechanisms that are altered and not just individual genes.

In this section we move towards discovering if our results are biologically significant. Are the genes that we have picked statistical flukes, or are there some commonalities.

There are two different approaches one might use, and we will highlight the theory behind both

Over-representation analysis

  • “Threshold-based” methods require defintion of a statistical threshold to define list of genes to test (e.g. FDR < 0.01)

Let’s take the set of genes H2AFY, SPRY2, CIB1, WNK1, SPRED1, SPRED2. With the exception of one, they all look to have significant p-value. But is this evidence enough that the whole pathway has been disturbed?

The question we are asking here is;

“Are the number of differentially expressed (DE) genes associated with Theme X significantly greater than what we might expect by chance alone?”

We can answer this question by knowing

  • the total number of DE genes
  • the number of genes in your gene set
  • the number of gnes in your gene set that are found to be DE
  • the total number of tested genes

The formula for Fishers exact test is;

\[ p = \frac{\binom{a + b}{a}\binom{c +d}{c}}{\binom{n}{a +c}} = \frac{(a+b)!(c+d)!(a+c)!(b+d)!}{a!b!c!d!n!} \]

with :-

In DE List Not In DE List Total
In Gene Set a b a + b
Not in Gene Set c d c + d
Total a + c b +d a + b + c + d (=n)

In this first test, our genes will be grouped together according to their Gene Ontology (GO) terms:- http://www.geneontology.org/

Software for conducting a over-representation test (goseq)

We will be using goseq, which is a software package available through Bioconductor. However, rather having to write R code, we will be using the package through our institute’s Galaxy server.

This package has been specifically-developed for use with RNA-seq data. Plenty of methods have been applied to microarray data, but the assumptions might not hold for RNA-seq. For instance, goseq will adjust for the length of a gene.

Preparing the data for an over-representation test

We can start with the list that contains results for all genes; Let’s take t47d_Treatment_DEA_Prog-vs-Control_all.csv

The goal is to obtain a table with two columns; the first containing the gene identifier, and the second being TRUE or FALSE indicating whether the gene is differentially-expressed at a given cut-off (e.g. 0.05). We don’t have to be too rigourous when deciding the p-value cut-off, as we’re using it here as a means to filter the data not the end-point of our analysis (I’ve seen some people use a threshold as high as 0.1).

If you wish to see the R code (using dplyr) for manipulating our data to provide an input file for the gene set analysis, click the ‘Code’ button on the right.

library(dplyr)
deTable <- read.csv("t47d_Treatment_DEA_Prog-vs-Control_all.csv")
mutate(deTable, DE = padj < 0.05) %>% 
  mutate(DE = ifelse(is.na(DE),FALSE,DE)) %>% 
  dplyr:::select(X, DE) -> filteredTable
  write.table(filteredTable,"de-table-for-goseq.txt",quote=FALSE,row.names=FALSE)
filteredTable

Manipulating the file can also be done in Galaxy.

Galaxy Workflow

Import the csv file

Get Data -> Upload Data

Convert to tabular form

Text Manipulation -> Convert delimiters to TAB

Select Commas from drop-down menu

Remove header line

Text Manipulation -> Select last lines from a dataset

Make note of how many lines in file, N and remove N-1 last lines with this tool

Retain columns of interest

Put c1,c2,c3,c4,c5,c6,c7,c12 to retain columns 1,2,3,4,5,6,7 and 12

Text Manipulation -> Cut columns from a table

The cleaned dataset

The output from the previous step will be referred to as our cleaned dataset and we will use it later on for another type of downstream analysis.

We now need to compute if each gene is DE at 0.05 significance level

Adding an extra column

Text Manipulation -> Compute an expression on every row

Use the expression c7<0.05 to test if the adjusted p-value is less than 0.05

Which should give something like this

Extracting the columns needed for goseq

Text Manipulation -> Cut columns from a table Choose c1,c9 as the columns to cut; the ensembl ID and the column created in the previous step

Which should give the following

Running goseq

NGS:RNA-seq -> goseq

Select Genome version hg19 in this case. You could also specify which type of identifiers can be found in the first column. Ensembl ID is the default and correct for our dataset.

After a while, you should notification that your analyis has completed and you can download the results by clicking the floppy disk icon.

Things to try:

  • Filter the output from the goseq analysis to gene sets with p-value less than 0.05

The differentially-expressed genes that we identified could either by up- or down-regulated. Sometimes we might want to look for over-represented genes in the up- or down-regulated genes only

  • Let’s suppose we want to analyse the up-regulated genes only
  • Consider what changes you would need to make to the workflow and perform the analysis

Threshold-free analysis

For these tests, we don’t have to specify a statistical threshold and use the test statistics from all genes as the input to the test. The popular Gene Set Enrichment Analysis (GSEA) method uses this approach. These tests can be used to detect differential expression for a group of genes, even when the effects are too small or there is too little data to detect the genes individually.

Let’s say that we have 10 genes “of interest”. We can look-up their statistics from the table


Attaching package: ‘limma’

The following object is masked from ‘package:BiocGenerics’:

    plotMA

Some of the statistics look high, but how do they compare to the rest of the genes? Are they towards the top of the list, or scattered randomly? This is what GSEA tries to determine. We are not making any claims about any particular gene in the list being DE, only that the set as a whole is altered.

We typically visualise the results of the analysis as a barcode plot. Firstly, all the test statistics are ranked from highest to lowest. We then draw a vertical line at every point where a gene in our list of interest is observed. We then determine where the peak of the distribution is, and consequently whether the genes are towards to top or bottom of the ranked list.

Here is the plot for our set:-

The case where we have a down-regulated pathway might look something like:-

Or in the case of no enrichment, there is no particular pattern to where the genes occur in the ranked list

The Broad institute provides a version of GSEA that can be run via a java application. The input to GSEA is matrix of expression values / counts and a file describing which covariate each sample belongs to. However, whether or not the method can be applied directly to RNA-seq data is under debate. According to the GSEA faq

The GSEA team has yet to determine whether any of these ranking statistics, originally selected for their effectiveness when used with expression data derived from DNA Microarray experiments, are appropriate for use with expression data derived from RNA-seq experiments. We hopefully will be able to devote some time to investigating this, but in the mean time, we are recommending use of the GSEAPreranked tool for conducting gene set enrichment analysis of data derived from RNA-seq experiments.

The GSEAPreranked tool mentioned is available through the Broad insitute’s GenePattern analysis suite. However, we don’t want to introduce yet another analysis suite, so we will focus on how to perform this analysis in Galaxy.

GSEA uses a set of pre-defined gene sets in their analysis which come under the following categories:-

  • H hallmark gene sets
  • C1 positional gene sets
  • C2 curated gene sets
  • C3 motif gene sets
  • C4 computational gene sets
  • C5 GO gene sets
  • C6 oncogenic signatures
  • C7 immunologic signatures

You can find out more about these gene sets on the msigdb website. However, it is important that these gene sets have Human gene symbols only.

Preparing the gene set for a GSEA analysis

For those interested, here is the R code to produce a file suitable for GSEA analysis:-

library(dplyr)
deTable <- read.csv("t47d_Treatment_DEA_Prog-vs-Control_all.csv")
deTable %>% arrange(desc(stat)) %>% 
  dplyr:::select(symbol, stat) %>% 
  filter(!is.na(symbol)) -> orderedTable
orderedTable

Now lets walkthrough how to do that in Galaxy:-

Ranking the genes

From the cleaned table created above, sort on column 5 (the test statistic)

Filter and Sort -> Sort data in ascending or descending order

Giving the output:-

We now have all the information we need, but the GSEA tool expects only two columns; gene symbol and ranked statistic

Cutting the columns

Extract columns 8 (gene symbol) followed by test statistic; c8,c5

Text Manipulation -> Cut columns from a table

Running a GSEA analysis

Using the fGSEA tool

fGSEA is a Bioconductor package that implements the pre-ranked GSEA analysis. It also happens to be a faster implementation, as described in their pre-print

You can find the tool under NGS: RNA-seq Analysis

With this tool, three different outputs are produced

  • a results table
  • a summary graphic of the top pathways
  • enrichment plots for the top pathways

Things to try:

  • Can you identify pathways that are significant at 0.05 level?
  • What pathways are over- and under-represented?
LS0tCnRpdGxlOiAiQmV5b25kIHRoZSBHZW5lIExpc3QiCmF1dGhvcjogIk1hcmsgRHVubmluZzsgbWFyayAnZG90JyBkdW5uaW5nICdhdCcgY3J1ay5jYW0uYWMudWsiCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLGVjaG89RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRkFMU0UpCmBgYAoKIyBIb3cgdG8gbWFuaXB1bGF0ZSBhIGdlbmUgbGlzdAoKVGhlIGdvbGRlbiBydWxlIGJlZm9yZSB3ZSBzdGFydCAqKipBbHdheXMga2VlcCBhIGJhY2t1cCBvZiB0aGUgb3V0cHV0IGZyb20gdGhlIHBpcGVsaW5lLCBuZXZlciB3b3JrIG9uIHRoZSBvcmlnaW5hbCBkYXRhKioqIQoKVGhlcmUgYXJlIHZhcmlvdXMgdGFza3MgeW91IG1pZ2h0IHdpc2ggdG8gcGVyZm9ybSBvbiBhIGdlbmUgbGlzdAoKLSBTZWFyY2ggZm9yIG91ciBmYXZvdXJpdGUgZ2VuZQotIFNvcnQgLyBSYW5rIGFjY29yZGluZyB0byBzdGF0aXN0aWMgb3IgcC12YWx1ZQotIEZpbHRlciB0byBvYnRhaW4gZ2VuZXMgd2l0aCBwYXJ0aWN1bGFyIGN1dC1vZmYgYW5kIGZvbGQtY2hhbmdlCi0gUmUtb3JkZXIgLyByZS1hcnJhbmdlIGNvbHVtbnMKCk91ciByZWNvbW1lbmRlZCB0b29scyB0byBwZXJmb3JtIHN1Y2ggb3BlcmF0aW9ucyB3b3VsZCBiZSBhIHByb2dyYW1taW5nIGxhbmd1YWdlIHN1Y2ggYXMgUiAvIFB5dGhvbiBzbyB0aGF0IHRoZSBvcGVyYXRpb25zIGNhbiBiZSBzY3JpcHRlZCBhbmQgYXV0b21hdGVkLiBBcyBhIGNvbXByb21pc2UsIG91ciBHYWxheHkgc2VydmVyIHByb3ZpZGVzIHZhcmlvdXMgb3BlcmF0aW9ucyB0aHJvdWdoIGFuIGludHVpdGl2ZSBpbnRlcmZhY2UgYW5kIGFsbG93cyB0aGUgdXNlciB0byBidWlsZCBhIHdvcmtmbG93IHRvIGEgY2hhaW4gb2Ygc2ltcGxlIG9wZXJhdGlvbnMuCgpUaGVyZSBpcyBhIHNwZWNpYWwgcmVhc29uIHdoeSB5b3Ugc2hvdWxkbid0IHRydXN0IEV4Y2VsIGFzIHlvdXIgbWFueSBtZWFucyBvZiBpbnRlcnJvZ2F0aW5nIHRoZSByZXN1bHRzLiBTb21ldGltZXMgaXQgY2FuIGJlIHVuaGVscGZ1bCBhbmQgY29udmVydCBnZW5lIGlkZW50aWZlcnMgaW50byBkYXRlcy4gW05vLCByZWFsbHkhXShodHRwczovL2dlbm9tZWJpb2xvZ3kuYmlvbWVkY2VudHJhbC5jb20vYXJ0aWNsZXMvMTAuMTE4Ni9zMTMwNTktMDE2LTEwNDQtNykKCldlIHdpbGwgb25seSBzY3JhdGNoIHRoZSBzdXJmYWNlIG9mIHdoYXQgaXMgcG9zc2libGUgd2l0aCBHYWxheHkuIElmIHlvdSBhcmUgbmV3IHRvIHRoZSBzb2Z0d2FyZSwgeW91IGNhbiBjaGVjayBvdXQgb3VyIGNvdXJzZTotIFtodHRwOi8vZ2FsYXh5Y2FtLmdpdGh1Yi5pby9dKGh0dHA6Ly9nYWxheHljYW0uZ2l0aHViLmlvLykKCkRpZmZlcmVudCBvbmxpbmUgdG9vbHMgb3IgR1VJcyB3aWxsIHRha2UgZGlmZmVyZW50IHR5cGVzIG9mIGlucHV0LCBidXQgd2lsbCBwcm9iYWJseSBpbnZvbHZlIHNvbWUgY29tYmluYXRpb24gb2YgdGhlIG9wZXJhdGlvbnMgbWVudGlvbmVkIGFib3ZlLiBUaGVyZWZvcmUsIHdlIHdpbGwgaW50cm9kdWNlIHNvbWUgdG9vbHMgaW4gR2FsYXh5IHRoYXQgc2hvdWxkIGFsbG93IHlvdSB0byBtYW5pcHVsYXRlIHlvdXIgZGF0YSBpbnRvIHRoZSByZXF1aXJlZCBmb3JtLiBMYXRlciwgd2Ugd2lsbCBnaXZlIGV4YW1wbGVzIG9mIHdvcmtmbG93cyBmb3IgcGFydGljdWxhciBnZW5lIHNldCBlbnJpY2htZW50IC8gcGF0aHdheXMgYW5hbHlzaXMgdG9vbHMuCgpJbiB0aGUgZmlyc3QgZ2VuZXJpYyBleGFtcGxlLCB3ZSB3aWxsIHNob3cgc29tZSBjb21tb24gbWFuaXB1bGF0aW9ucyBvbiBhIGdlbmUgbGlzdCB1c2luZyBHYWxheHk6LQoKIyMgQ29tbW9uIHRhc2tzIGluIEdhbGF4eQoKIyMjIFVwbG9hZCB0aGUgZ2VuZSBsaXN0IGluIGNzdiBmb3JtCgpUaGUgZ2VuZSBsaXN0IGAuY3N2YCBmaWxlIGNhbiBiZSB1cGxvYWRlZCBpbnRvIEdhbGF4eS4gSW4gdGhpcyBleGFtcGxlIHdlIHdhbnQgdG8gY2hvb3NlIHRoZSBmaWxlIHdpdGggbm8gcC12YWx1ZSBjdXQtb2ZmcyBhcHBsaWVkCgohW10oaW1hZ2VzL2ZpbHRlcjEucG5nKQoKIyMjIENvbnZlcnQgdG8gdGFidWxhcgoKQmVmb3JlIHdlIGNhbiBnbyBmdXJ0aGVyLCBHYWxheHkgbmVlZHMgdG8gY29udmVydCB0aGUgZGF0YSB5b3UganVzdCB1cGxvYWRlZCBpbnRvICp0YWJ1bGFyKiBmb3JtIGJ5IHJlcGxhY2luZyBjb21tYXMgd2l0aCBhIHRhYi4gVGhpcyBjYW4gYmUgZG9uZSB1c2luZyB0aGUgKioqVGV4dCBNYW5pcHVsYXRpb24qKiogLT4gKioqQ29udmVydCBkZWxpbWl0ZXJzIHRvIFRBQioqKiBvcHRpb24uIE1ha2Ugc3VyZSB0aGF0ICpDb21tYXMqIGlzIHNlbGVjdGVkIGZyb20gdGhlIGRyb3AtZG93bi4KCiFbXShpbWFnZXMvZmlsdGVyMi5wbmcpCgojIyMgRmlsdGVyCgpXZSBjYW4gYXBwbHkgZmlsdGVyaW5nIHVzaW5nIHRoZSBtZW51IG9wdGlvbiAqKipGaWx0ZXIgYW5kIFNvcnQqKiogLT4gKioqRmlsdGVyIGRhdGEgb24gYW55IGNvbHVtbiB1c2luZyBzaW1wbGUgZXhwcmVzc2lvbnMqKiouIEhlcmUgd2UgdXNlIHRoZSBjb25kaXRpb24gYGM3IDwwLjAxIGFuZCBjMyA+IDEuNWAgdG8gcmVxdWlyZSB0aGF0IENvbHVtbiA3IChhZGp1c3RlZCBwLXZhbHVlKSBpcyBsZXNzIHRoYW4gMC4wNSAqYW5kKiBDb2x1bW4gMyAobG9nIGZvbGQtY2hhbmdlKSBpcyBncmVhdGVyIHRoYW4gMS41LgoKCiFbXShpbWFnZXMvZmlsdGVyMy5wbmcpCgpZb3UgY291bGQgYWxzbyB1c2UgdGhpcyBtZW51IHRvIGZpbHRlciBvbiBnZW5lcyBvbiBhIHBhcnRpY3VsYXIgY2hyb21vc29tZSBvciB3aXRoaW4gYSBjZXJ0YWluIHJhbmdlIG9mIHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25zCgojIyBTZWxlY3RpbmcgY29sdW1ucwoKSWYgeW91IGZpbmQgdGhlIG51bWJlciBvZiBjb2x1bW5zIGluIHRoZSBmaWxlIGEgYml0IHVud2llbGR5LCB5b3UgY2FuIGFsc28gdXNlIHRoZSAqKipUZXh0IE1hbmlwdWxhdGlvbioqKiAtPiAqKipDdXQgY29sdW1ucyBmcm9tIGEgdGFibGUqKiogdG8gcmVtb3ZlIHNvbWUgY29sdW1ucywgb3IgcmUtb3JkZXIgdGhlIGV4aXN0aW5nIG9uZXM6LQoKIVtdKGltYWdlcy9maWx0ZXI0LnBuZykKCiMjIFNvcnRpbmcgYnkgYSBjb2x1bW4KCldlIGNhbiBhbHNvIHNvcnQgdGhlIHRhYmxlIGJ5IHZhbHVlcyBpbiBhIHBhcnRpY3VsYXIgY29sdW1uLiBlLmcuIHRoZSBsb2ckXzIkIGZvbGQgY2hhbmdlIHdoaWNoIGlzIGZvdW5kIGluIGNvbHVtbiBudW1iZXIgMy4KIVtdKGltYWdlcy9maWx0ZXI1LnBuZykKCi0gY2FuIGFsc28gc29ydCBieSBtdWx0aXBsZSBjb2x1bW5zCiAgICArIGUuZy4gQ2hyb21vc29tZSwgYW5kIHRoZW4gc3RhcnQKCiAKIyMgUiB3b3JrZmxvdwoKRm9yIHRob3NlIGtlZW4gb24gUiwgZXF1aXZhbGVudCBvcGVyYXRpb25zIGNhbiBiZSBwZXJmb3JtZWQgd2l0aCB0aGUgYGRwbHlyYCBwYWNrYWdlLiBTZWUgb3VyIFtpbnRlcm1lZGlhdGUgUl0oaHR0cDovL2Jpb2luZm9ybWF0aWNzLWNvcmUtc2hhcmVkLXRyYWluaW5nLmdpdGh1Yi5pby9yLWludGVybWVkaWF0ZS8pIGNvdXJzZSBmb3IgZGV0YWlscy4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpkZVRhYmxlIDwtIHJlYWQuY3N2KCJ0NDdkX1RyZWF0bWVudF9ERUFfUHJvZy12cy1Db250cm9sX2FsbC5jc3YiKQpmaWx0ZXJlZFRhYmxlIDwtIGZpbHRlcihkZVRhYmxlLCBwYWRqIDwgMC4wNSwgbG9nMkZvbGRDaGFuZ2UgPiAxLjUpCnNvcnRlZFRhYmxlIDwtIGFycmFuZ2UoZmlsdGVyZWRUYWJsZSwgbG9nMkZvbGRDaGFuZ2UpCnJlZHVjZWRUYWJsZSA8LSBzZWxlY3QoZmlsdGVyZWRUYWJsZSwgWCxiYXNlTWVhbixsb2cyRm9sZENoYW5nZSkKYGBgCgojIEdlbmUtT250b2xvZ2llcyBhbmQgUGF0aHdheXMKCgpJbiB0aGUgZWFybHkgZGF5cyBvZiBtaWNyb2FycmF5IGFuYWx5c2lzLCBwZW9wbGUgd2VyZSBoYXBweSBpZiB0aGV5IGdvdCBhIGhhbmRmdWwgb2YgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIGdlbmVzIHRoYXQgdGhleSBjb3VsZCB2YWxpZGF0ZSBvciBmb2xsb3ctdXAuIEhvd2V2ZXIsIHdpdGggbGF0ZXIgdGVjaG5vbG9naWVzIChhbmQgZGVwZW5kaW5nIG9uIHRoZSBleHBlcmltZW50YWwgc2V0dXApIHdlIG1pZ2h0IGhhdmUgdGhvdXNhbmRzIG9mIHN0YXRpc3RpY2FsbHktc2lnbmlmaWNhbnQgcmVzdWx0cywgd2hpY2ggbm8tb25lIGhhcyB0aGUgdGltZSB0byBmb2xsb3ctdXAuIEFsc28sIHdlIG1pZ2h0IGJlIGludGVyZXN0ZWQgaW4gcGF0aHdheXMgLyBtZWNoYW5pc21zIHRoYXQgYXJlIGFsdGVyZWQgYW5kIG5vdCBqdXN0IGluZGl2aWR1YWwgZ2VuZXMuCgpJbiB0aGlzIHNlY3Rpb24gd2UgbW92ZSB0b3dhcmRzIGRpc2NvdmVyaW5nIGlmIG91ciByZXN1bHRzIGFyZSAqKipiaW9sb2dpY2FsbHkgc2lnbmlmaWNhbnQqKiouIEFyZSB0aGUgZ2VuZXMgdGhhdCB3ZSBoYXZlIHBpY2tlZCBzdGF0aXN0aWNhbCBmbHVrZXMsIG9yIGFyZSB0aGVyZSBzb21lIGNvbW1vbmFsaXRpZXMuIAoKVGhlcmUgYXJlIHR3byBkaWZmZXJlbnQgYXBwcm9hY2hlcyBvbmUgbWlnaHQgdXNlLCBhbmQgd2Ugd2lsbCBoaWdobGlnaHQgdGhlIHRoZW9yeSBiZWhpbmQgYm90aAoKIyMgIE92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMKCi0gIlRocmVzaG9sZC1iYXNlZCIgbWV0aG9kcyByZXF1aXJlIGRlZmludGlvbiBvZiBhIHN0YXRpc3RpY2FsIHRocmVzaG9sZCB0byBkZWZpbmUgbGlzdCBvZiBnZW5lcyB0byB0ZXN0IChlLmcuIEZEUiA8IDAuMDEpCgpgYGB7ciBtZXNzYWdlPUZBTFNFLGVjaG89RkFMU0V9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KG9yZy5Icy5lZy5kYikpCnJlc3VsdCA8LSBzZWxlY3Qob3JnLkhzLmVnLmRiLCBrZXlzPSJHTzowMDMwMjkxIixrZXl0eXBlID0gIkdPIixjb2x1bW5zPSJTWU1CT0wiKQpteWdlbmVzIDwtIHJlc3VsdCRTWU1CT0wKYGBgCgpMZXQncyB0YWtlIHRoZSBzZXQgb2YgZ2VuZXMgYHIgcGFzdGUobXlnZW5lcywgY29sbGFwc2U9IiwgIilgLiBXaXRoIHRoZSBleGNlcHRpb24gb2Ygb25lLCB0aGV5IGFsbCBsb29rIHRvIGhhdmUgc2lnbmlmaWNhbnQgcC12YWx1ZS4gQnV0IGlzIHRoaXMgZXZpZGVuY2UgZW5vdWdoIHRoYXQgdGhlIHdob2xlIHBhdGh3YXkgaGFzIGJlZW4gZGlzdHVyYmVkPwoKYGBge3IgZWNobz1GQUxTRX0KZHBseXI6OjpmaWx0ZXIoZGVUYWJsZSwgc3ltYm9sICVpbiUgbXlnZW5lcykKYGBgCgpUaGUgcXVlc3Rpb24gd2UgYXJlIGFza2luZyBoZXJlIGlzOwoKPiAqKioiQXJlIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIChERSkgZ2VuZXMgYXNzb2NpYXRlZCB3aXRoIFRoZW1lIFggc2lnbmlmaWNhbnRseSBncmVhdGVyIHRoYW4gd2hhdCB3ZSBtaWdodCBleHBlY3QgYnkgY2hhbmNlIGFsb25lPyIqKioKCldlIGNhbiBhbnN3ZXIgdGhpcyBxdWVzdGlvbiBieSBrbm93aW5nCgotIHRoZSB0b3RhbCBudW1iZXIgb2YgREUgZ2VuZXMKLSB0aGUgbnVtYmVyIG9mIGdlbmVzIGluIHlvdXIgZ2VuZSBzZXQKLSB0aGUgbnVtYmVyIG9mIGduZXMgaW4geW91ciBnZW5lIHNldCB0aGF0IGFyZSBmb3VuZCB0byBiZSBERQotIHRoZSB0b3RhbCBudW1iZXIgb2YgdGVzdGVkIGdlbmVzCgpUaGUgZm9ybXVsYSBmb3IgRmlzaGVycyBleGFjdCB0ZXN0IGlzOwoKJCQgcCA9IFxmcmFje1xiaW5vbXthICsgYn17YX1cYmlub217YyArZH17Y319e1xiaW5vbXtufXthICtjfX0gPSBcZnJhY3soYStiKSEoYytkKSEoYStjKSEoYitkKSF9e2EhYiFjIWQhbiF9ICQkCgp3aXRoIDotCgp8ICB8IEluIERFIExpc3QgfCBOb3QgSW4gREUgTGlzdCAgfCBUb3RhbAotLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLSB8ICAtLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLSB8IApJbiBHZW5lIFNldCB8IGEgfCBiIHwgYSAgKyAgYiB8Ck5vdCBpbiBHZW5lIFNldCAgfCBjIHwgZCB8IGMgKyBkIHwKVG90YWwgfCBhICsgYyB8IGIgK2QgfCBhICsgYiArIGMgKyBkICg9bikgfAoKSW4gdGhpcyBmaXJzdCB0ZXN0LCBvdXIgZ2VuZXMgd2lsbCBiZSBncm91cGVkIHRvZ2V0aGVyIGFjY29yZGluZyB0byB0aGVpciBHZW5lIE9udG9sb2d5ICgqR08qKSB0ZXJtczotIFtodHRwOi8vd3d3LmdlbmVvbnRvbG9neS5vcmcvXShodHRwOi8vd3d3LmdlbmVvbnRvbG9neS5vcmcvKQoKIyMgU29mdHdhcmUgZm9yIGNvbmR1Y3RpbmcgYSBvdmVyLXJlcHJlc2VudGF0aW9uIHRlc3QgKGdvc2VxKQoKV2Ugd2lsbCBiZSB1c2luZyBbYGdvc2VxYF0oaHR0cHM6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9nb3NlcS5odG1sKSwgd2hpY2ggaXMgYSBzb2Z0d2FyZSBwYWNrYWdlIGF2YWlsYWJsZSB0aHJvdWdoIFtCaW9jb25kdWN0b3JdKHd3dy5iaW9jb25kdWN0b3Iub3JnLykuIEhvd2V2ZXIsIHJhdGhlciBoYXZpbmcgdG8gd3JpdGUgUiBjb2RlLCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBwYWNrYWdlIHRocm91Z2ggb3VyIGluc3RpdHV0ZSdzIEdhbGF4eSBzZXJ2ZXIuCgpUaGlzIHBhY2thZ2UgaGFzIGJlZW4gKnNwZWNpZmljYWxseS1kZXZlbG9wZWQqIGZvciB1c2Ugd2l0aCBSTkEtc2VxIGRhdGEuIFBsZW50eSBvZiBtZXRob2RzIGhhdmUgYmVlbiBhcHBsaWVkIHRvIG1pY3JvYXJyYXkgZGF0YSwgYnV0IHRoZSBhc3N1bXB0aW9ucyBtaWdodCBub3QgaG9sZCBmb3IgUk5BLXNlcS4gRm9yIGluc3RhbmNlLCBgZ29zZXFgIHdpbGwgW2FkanVzdCBmb3IgdGhlIGxlbmd0aCBvZiBhIGdlbmVdKGh0dHA6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvZ2ItMjAxMC0xMS0yLXIxNCkuCgojIyBQcmVwYXJpbmcgdGhlIGRhdGEgZm9yIGFuIG92ZXItcmVwcmVzZW50YXRpb24gdGVzdAoKV2UgY2FuIHN0YXJ0IHdpdGggdGhlIGxpc3QgdGhhdCBjb250YWlucyByZXN1bHRzIGZvciAqYWxsKiBnZW5lczsgTGV0J3MgdGFrZSBgdDQ3ZF9UcmVhdG1lbnRfREVBX1Byb2ctdnMtQ29udHJvbF9hbGwuY3N2YAoKClRoZSBnb2FsIGlzIHRvIG9idGFpbiBhIHRhYmxlIHdpdGggdHdvIGNvbHVtbnM7IHRoZSBmaXJzdCBjb250YWluaW5nIHRoZSBnZW5lIGlkZW50aWZpZXIsIGFuZCB0aGUgc2Vjb25kIGJlaW5nIGBUUlVFYCBvciBgRkFMU0VgIGluZGljYXRpbmcgd2hldGhlciB0aGUgZ2VuZSBpcyBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgYXQgYSBnaXZlbiBjdXQtb2ZmIChlLmcuIGAwLjA1YCkuIFdlIGRvbid0IGhhdmUgdG8gYmUgdG9vIHJpZ291cm91cyB3aGVuIGRlY2lkaW5nIHRoZSBwLXZhbHVlIGN1dC1vZmYsIGFzIHdlJ3JlIHVzaW5nIGl0IGhlcmUgYXMgYSBtZWFucyB0byBmaWx0ZXIgdGhlIGRhdGEgbm90IHRoZSBlbmQtcG9pbnQgb2Ygb3VyIGFuYWx5c2lzIChJJ3ZlIHNlZW4gc29tZSBwZW9wbGUgdXNlIGEgdGhyZXNob2xkIGFzIGhpZ2ggYXMgMC4xKS4KCklmIHlvdSB3aXNoIHRvIHNlZSB0aGUgUiBjb2RlICh1c2luZyBgZHBseXJgKSBmb3IgbWFuaXB1bGF0aW5nIG91ciBkYXRhIHRvIHByb3ZpZGUgYW4gaW5wdXQgZmlsZSBmb3IgdGhlIGdlbmUgc2V0IGFuYWx5c2lzLCBjbGljayB0aGUgJ0NvZGUnIGJ1dHRvbiBvbiB0aGUgcmlnaHQuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpkZVRhYmxlIDwtIHJlYWQuY3N2KCJ0NDdkX1RyZWF0bWVudF9ERUFfUHJvZy12cy1Db250cm9sX2FsbC5jc3YiKQptdXRhdGUoZGVUYWJsZSwgREUgPSBwYWRqIDwgMC4wNSkgJT4lIAogIG11dGF0ZShERSA9IGlmZWxzZShpcy5uYShERSksRkFMU0UsREUpKSAlPiUgCiAgZHBseXI6OjpzZWxlY3QoWCwgREUpIC0+IGZpbHRlcmVkVGFibGUKICB3cml0ZS50YWJsZShmaWx0ZXJlZFRhYmxlLCJkZS10YWJsZS1mb3ItZ29zZXEudHh0IixxdW90ZT1GQUxTRSxyb3cubmFtZXM9RkFMU0UpCmZpbHRlcmVkVGFibGUKYGBgCgpNYW5pcHVsYXRpbmcgdGhlIGZpbGUgY2FuIGFsc28gYmUgZG9uZSBpbiBHYWxheHkuCgojIyBHYWxheHkgV29ya2Zsb3cKCiMjIyBJbXBvcnQgdGhlIGNzdiBmaWxlCgoqKipHZXQgRGF0YSoqKiAtPiAqKipVcGxvYWQgRGF0YSoqKgoKIVtdKGltYWdlcy9nYWxheHktMS5wbmcpCgojIyMgQ29udmVydCB0byB0YWJ1bGFyIGZvcm0KCioqKlRleHQgTWFuaXB1bGF0aW9uKioqIC0+ICoqKkNvbnZlcnQgZGVsaW1pdGVycyB0byBUQUIqKioKClNlbGVjdCAqQ29tbWFzKiBmcm9tIGRyb3AtZG93biBtZW51CgohW10oaW1hZ2VzL2dhbGF4eS0yLnBuZykKCiMjIyBSZW1vdmUgaGVhZGVyIGxpbmUKCioqKlRleHQgTWFuaXB1bGF0aW9uKioqIC0+ICoqKlNlbGVjdCBsYXN0IGxpbmVzIGZyb20gYSBkYXRhc2V0KioqCgpNYWtlIG5vdGUgb2YgaG93IG1hbnkgbGluZXMgaW4gZmlsZSwgKk4qIGFuZCByZW1vdmUgKk4tMSogbGFzdCBsaW5lcyB3aXRoIHRoaXMgdG9vbAohW10oaW1hZ2VzL2dhbGF4eS0zLnBuZykKCiMjIyBSZXRhaW4gY29sdW1ucyBvZiBpbnRlcmVzdAoKUHV0ICpjMSxjMixjMyxjNCxjNSxjNixjNyxjMTIqIHRvIHJldGFpbiBjb2x1bW5zIDEsMiwzLDQsNSw2LDcgYW5kIDEyCgoqKipUZXh0IE1hbmlwdWxhdGlvbioqKiAtPiAqKipDdXQgY29sdW1ucyBmcm9tIGEgdGFibGUqKioKCiFbXShpbWFnZXMvZ2FsYXh5LTQucG5nKQoKIyMjIFRoZSBjbGVhbmVkIGRhdGFzZXQKClRoZSBvdXRwdXQgZnJvbSB0aGUgcHJldmlvdXMgc3RlcCB3aWxsIGJlIHJlZmVycmVkIHRvIGFzIG91ciAqY2xlYW5lZCBkYXRhc2V0KiBhbmQgd2Ugd2lsbCB1c2UgaXQgbGF0ZXIgb24gZm9yIGFub3RoZXIgdHlwZSBvZiBkb3duc3RyZWFtIGFuYWx5c2lzLgoKIVtdKGltYWdlcy9nYWxheHktNS5wbmcpCgoKV2Ugbm93IG5lZWQgdG8gY29tcHV0ZSBpZiBlYWNoIGdlbmUgaXMgREUgYXQgMC4wNSBzaWduaWZpY2FuY2UgbGV2ZWwKCiMjIyBBZGRpbmcgYW4gZXh0cmEgY29sdW1uCgoqKipUZXh0IE1hbmlwdWxhdGlvbioqKiAtPiAqKipDb21wdXRlIGFuIGV4cHJlc3Npb24gb24gZXZlcnkgcm93KioqCgpVc2UgdGhlIGV4cHJlc3Npb24gYGM3PDAuMDVgIHRvIHRlc3QgaWYgdGhlIGFkanVzdGVkIHAtdmFsdWUgaXMgbGVzcyB0aGFuIDAuMDUKCiFbXShpbWFnZXMvZ2FsYXh5LTYucG5nKQoKV2hpY2ggc2hvdWxkIGdpdmUgc29tZXRoaW5nIGxpa2UgdGhpcwoKIVtdKGltYWdlcy9nYWxheHktNy5wbmcpCgojIyMgRXh0cmFjdGluZyB0aGUgY29sdW1ucyBuZWVkZWQgZm9yIGdvc2VxCgoqKipUZXh0IE1hbmlwdWxhdGlvbioqKiAtPiAqKipDdXQgY29sdW1ucyBmcm9tIGEgdGFibGUqKioKQ2hvb3NlIGBjMSxjOWAgYXMgdGhlIGNvbHVtbnMgdG8gY3V0OyB0aGUgZW5zZW1ibCBJRCBhbmQgdGhlIGNvbHVtbiBjcmVhdGVkIGluIHRoZSBwcmV2aW91cyBzdGVwCgohW10oaW1hZ2VzL2dhbGF4eS04LnBuZykKCldoaWNoIHNob3VsZCBnaXZlIHRoZSBmb2xsb3dpbmcKCiFbXShpbWFnZXMvZ2FsYXh5LTkucG5nKQoKIyMjIFJ1bm5pbmcgZ29zZXEKCioqKk5HUzpSTkEtc2VxKioqIC0+ICoqKmdvc2VxKioqCgpTZWxlY3QgR2Vub21lIHZlcnNpb24gKmhnMTkqIGluIHRoaXMgY2FzZS4gWW91IGNvdWxkIGFsc28gc3BlY2lmeSB3aGljaCB0eXBlIG9mIGlkZW50aWZpZXJzIGNhbiBiZSBmb3VuZCBpbiB0aGUgZmlyc3QgY29sdW1uLiAqRW5zZW1ibCogSUQgaXMgdGhlIGRlZmF1bHQgYW5kIGNvcnJlY3QgZm9yIG91ciBkYXRhc2V0LgoKIVtdKGltYWdlcy9nYWxheHktZ29zZXEucG5nKQoKQWZ0ZXIgYSB3aGlsZSwgeW91IHNob3VsZCBub3RpZmljYXRpb24gdGhhdCB5b3VyIGFuYWx5aXMgaGFzIGNvbXBsZXRlZCBhbmQgeW91IGNhbiBkb3dubG9hZCB0aGUgcmVzdWx0cyBieSBjbGlja2luZyB0aGUgZmxvcHB5IGRpc2sgaWNvbi4KCiFbXShpbWFnZXMvZ2FsYXh5LWdvc2VxLXNhdmUucG5nKQoKCiMjIyBUaGluZ3MgdG8gdHJ5OgoKLSBGaWx0ZXIgdGhlIG91dHB1dCBmcm9tIHRoZSBnb3NlcSBhbmFseXNpcyB0byBnZW5lIHNldHMgd2l0aCBwLXZhbHVlIGxlc3MgdGhhbiAwLjA1CgpUaGUgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIGdlbmVzIHRoYXQgd2UgaWRlbnRpZmllZCBjb3VsZCBlaXRoZXIgYnkgdXAtIG9yIGRvd24tcmVndWxhdGVkLiBTb21ldGltZXMgd2UgbWlnaHQgd2FudCB0byBsb29rIGZvciBvdmVyLXJlcHJlc2VudGVkIGdlbmVzIGluIHRoZSB1cC0gb3IgZG93bi1yZWd1bGF0ZWQgZ2VuZXMgb25seQoKLSBMZXQncyBzdXBwb3NlIHdlIHdhbnQgdG8gYW5hbHlzZSB0aGUgdXAtcmVndWxhdGVkIGdlbmVzIG9ubHkKLSBDb25zaWRlciB3aGF0IGNoYW5nZXMgeW91IHdvdWxkIG5lZWQgdG8gbWFrZSB0byB0aGUgd29ya2Zsb3cgYW5kIHBlcmZvcm0gdGhlIGFuYWx5c2lzCgojIyBUaHJlc2hvbGQtZnJlZSBhbmFseXNpcwoKRm9yIHRoZXNlIHRlc3RzLCB3ZSBkb24ndCBoYXZlIHRvIHNwZWNpZnkgYSBzdGF0aXN0aWNhbCB0aHJlc2hvbGQgYW5kIHVzZSB0aGUgdGVzdCBzdGF0aXN0aWNzIGZyb20gKmFsbCogZ2VuZXMgYXMgdGhlIGlucHV0IHRvIHRoZSB0ZXN0LiBUaGUgcG9wdWxhciAqR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcyAoR1NFQSkqIG1ldGhvZCB1c2VzIHRoaXMgYXBwcm9hY2guIFRoZXNlIHRlc3RzIGNhbiBiZSB1c2VkIHRvIGRldGVjdCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBmb3IgYSBncm91cCBvZiBnZW5lcywgZXZlbiB3aGVuIHRoZSBlZmZlY3RzIGFyZSB0b28gc21hbGwgb3IgdGhlcmUgaXMgdG9vIGxpdHRsZSBkYXRhIHRvIGRldGVjdCB0aGUgZ2VuZXMgaW5kaXZpZHVhbGx5LgoKTGV0J3Mgc2F5IHRoYXQgd2UgaGF2ZSAxMCBnZW5lcyAib2YgaW50ZXJlc3QiLiBXZSBjYW4gbG9vay11cCB0aGVpciBzdGF0aXN0aWNzIGZyb20gdGhlIHRhYmxlCmBgYHtyIGVjaG89RkFMU0V9CmxpYnJhcnkobGltbWEpCmRlVGFibGUgPC0gcmVhZC5jc3YoInQ0N2RfVHJlYXRtZW50X0RFQV9Qcm9nLXZzLUNvbnRyb2xfYWxsLmNzdiIpCnN0YXRzIDwtIGRlVGFibGUkc3RhdApzdGF0c09yZGVyIDwtIG9yZGVyKHN0YXRzLGRlY3JlYXNpbmcgPSBUUlVFKQpteUluZGV4IDwtICBjKHNhbXBsZShzdGF0c09yZGVyWzE6NTAwXSw1KSxzYW1wbGUoNTAxOmxlbmd0aChzdGF0cyksNSkpCmRhdGEuZnJhbWUoR2VuZSA9IGRlVGFibGUkc3ltYm9sW215SW5kZXhdLCBTdGF0aXN0aWMgPSBzdGF0c1tteUluZGV4XSkKYGBgCgpTb21lIG9mIHRoZSBzdGF0aXN0aWNzIGxvb2sgaGlnaCwgYnV0IGhvdyBkbyB0aGV5IGNvbXBhcmUgdG8gdGhlIHJlc3Qgb2YgdGhlIGdlbmVzPyBBcmUgdGhleSB0b3dhcmRzIHRoZSB0b3Agb2YgdGhlIGxpc3QsIG9yIHNjYXR0ZXJlZCByYW5kb21seT8gVGhpcyBpcyB3aGF0IEdTRUEgdHJpZXMgdG8gZGV0ZXJtaW5lLiBXZSBhcmUgbm90IG1ha2luZyBhbnkgY2xhaW1zIGFib3V0IGFueSBwYXJ0aWN1bGFyIGdlbmUgaW4gdGhlIGxpc3QgYmVpbmcgREUsIG9ubHkgdGhhdCB0aGUgc2V0IGFzIGEgd2hvbGUgaXMgYWx0ZXJlZC4KCldlIHR5cGljYWxseSB2aXN1YWxpc2UgdGhlIHJlc3VsdHMgb2YgdGhlIGFuYWx5c2lzIGFzIGEgYmFyY29kZSBwbG90LiBGaXJzdGx5LCBhbGwgdGhlIHRlc3Qgc3RhdGlzdGljcyBhcmUgcmFua2VkIGZyb20gaGlnaGVzdCB0byBsb3dlc3QuIFdlIHRoZW4gZHJhdyBhIHZlcnRpY2FsIGxpbmUgYXQgZXZlcnkgcG9pbnQgd2hlcmUgYSBnZW5lIGluIG91ciBsaXN0IG9mIGludGVyZXN0IGlzIG9ic2VydmVkLiBXZSB0aGVuIGRldGVybWluZSB3aGVyZSB0aGUgKnBlYWsqIG9mIHRoZSBkaXN0cmlidXRpb24gaXMsIGFuZCBjb25zZXF1ZW50bHkgd2hldGhlciB0aGUgZ2VuZXMgYXJlIHRvd2FyZHMgdG8gdG9wIG9yIGJvdHRvbSBvZiB0aGUgcmFua2VkIGxpc3QuCgpIZXJlIGlzIHRoZSBwbG90IGZvciBvdXIgc2V0Oi0KCmBgYHtyIGVjaG89RkFMU0V9CmJhcmNvZGVwbG90KHN0YXRzLCBteUluZGV4KQpgYGAKClRoZSBjYXNlIHdoZXJlIHdlIGhhdmUgYSBkb3duLXJlZ3VsYXRlZCBwYXRod2F5IG1pZ2h0IGxvb2sgc29tZXRoaW5nIGxpa2U6LQoKYGBge3IgZWNobz1GQUxTRX0Kc3RhdHNSZXZPcmRlciA8LSBvcmRlcihzdGF0cyxkZWNyZWFzaW5nID0gRkFMU0UpCm15UmV2SW5kZXggPC0gIGMoc2FtcGxlKHN0YXRzUmV2T3JkZXJbMTo1MDBdLDUpLHNhbXBsZSg1MDE6bGVuZ3RoKHN0YXRzKSw1KSkKYmFyY29kZXBsb3Qoc3RhdHMsIG15UmV2SW5kZXgpCmBgYAoKT3IgaW4gdGhlIGNhc2Ugb2Ygbm8gZW5yaWNobWVudCwgdGhlcmUgaXMgbm8gcGFydGljdWxhciBwYXR0ZXJuIHRvIHdoZXJlIHRoZSBnZW5lcyBvY2N1ciBpbiB0aGUgcmFua2VkIGxpc3QKCmBgYHtyIGVjaG89RkFMU0V9CmJhcmNvZGVwbG90KHN0YXRzLCBzYW1wbGUoMTpsZW5ndGgoc3RhdHMpLDEwMCkpCmBgYAoKClRoZSBCcm9hZCBpbnN0aXR1dGUgcHJvdmlkZXMgW2EgdmVyc2lvbiBvZiBHU0VBXShodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvaW5kZXguanNwKSB0aGF0IGNhbiBiZSBydW4gdmlhIGEgamF2YSBhcHBsaWNhdGlvbi4gVGhlIGlucHV0IHRvIEdTRUEgaXMgbWF0cml4IG9mIGV4cHJlc3Npb24gdmFsdWVzIC8gY291bnRzIGFuZCBhIGZpbGUgZGVzY3JpYmluZyB3aGljaCBjb3ZhcmlhdGUgZWFjaCBzYW1wbGUgYmVsb25ncyB0by4gSG93ZXZlciwgd2hldGhlciBvciBub3QgdGhlIG1ldGhvZCBjYW4gYmUgYXBwbGllZCBkaXJlY3RseSB0byBSTkEtc2VxIGRhdGEgaXMgdW5kZXIgZGViYXRlLiBBY2NvcmRpbmcgdG8gdGhlIFtHU0VBIGZhcV0oaHR0cDovL3NvZnR3YXJlLmJyb2FkaW5zdGl0dXRlLm9yZy9jYW5jZXIvc29mdHdhcmUvZ3NlYS93aWtpL2luZGV4LnBocC9GQVEjQ2FuX0lfdXNlX0dTRUFfdG9fYW5hbHl6ZV9TTlAuMkNfU0FHRS4yQ19DaElQLVNlcV9vcl9STkEtU2VxX2RhdGEuM0YpCgo+IFRoZSBHU0VBIHRlYW0gaGFzIHlldCB0byBkZXRlcm1pbmUgd2hldGhlciBhbnkgb2YgdGhlc2UgcmFua2luZyBzdGF0aXN0aWNzLCBvcmlnaW5hbGx5IHNlbGVjdGVkIGZvciB0aGVpciBlZmZlY3RpdmVuZXNzIHdoZW4gdXNlZCB3aXRoIGV4cHJlc3Npb24gZGF0YSBkZXJpdmVkIGZyb20gRE5BIE1pY3JvYXJyYXkgZXhwZXJpbWVudHMsIGFyZSBhcHByb3ByaWF0ZSBmb3IgdXNlIHdpdGggZXhwcmVzc2lvbiBkYXRhIGRlcml2ZWQgZnJvbSBSTkEtc2VxIGV4cGVyaW1lbnRzLiBXZSBob3BlZnVsbHkgd2lsbCBiZSBhYmxlIHRvIGRldm90ZSBzb21lIHRpbWUgdG8gaW52ZXN0aWdhdGluZyB0aGlzLCBidXQgaW4gdGhlIG1lYW4gdGltZSwgd2UgYXJlIHJlY29tbWVuZGluZyB1c2Ugb2YgdGhlIEdTRUFQcmVyYW5rZWQgdG9vbCBmb3IgY29uZHVjdGluZyBnZW5lIHNldCBlbnJpY2htZW50IGFuYWx5c2lzIG9mIGRhdGEgZGVyaXZlZCBmcm9tIFJOQS1zZXEgZXhwZXJpbWVudHMuCgpUaGUgR1NFQVByZXJhbmtlZCB0b29sIG1lbnRpb25lZCBpcyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgQnJvYWQgaW5zaXR1dGUncyBbR2VuZVBhdHRlcm5dKGh0dHBzOi8vZ2VuZXBhdHRlcm4uYnJvYWRpbnN0aXR1dGUub3JnKSBhbmFseXNpcyBzdWl0ZS4gSG93ZXZlciwgd2UgZG9uJ3Qgd2FudCB0byBpbnRyb2R1Y2UgeWV0IGFub3RoZXIgYW5hbHlzaXMgc3VpdGUsIHNvIHdlIHdpbGwgZm9jdXMgb24gaG93IHRvIHBlcmZvcm0gdGhpcyBhbmFseXNpcyBpbiBHYWxheHkuCgpHU0VBIHVzZXMgYSBzZXQgb2YgcHJlLWRlZmluZWQgZ2VuZSBzZXRzIGluIHRoZWlyIGFuYWx5c2lzIHdoaWNoIGNvbWUgdW5kZXIgdGhlIGZvbGxvd2luZyBjYXRlZ29yaWVzOi0KCi0gSCBoYWxsbWFyayBnZW5lIHNldHMKLSBDMSBwb3NpdGlvbmFsIGdlbmUgc2V0cwotIEMyIGN1cmF0ZWQgZ2VuZSBzZXRzCi0gQzMgbW90aWYgZ2VuZSBzZXRzCi0gQzQgY29tcHV0YXRpb25hbCBnZW5lIHNldHMKLSBDNSBHTyBnZW5lIHNldHMKLSBDNiBvbmNvZ2VuaWMgc2lnbmF0dXJlcwotIEM3IGltbXVub2xvZ2ljIHNpZ25hdHVyZXMKCllvdSBjYW4gZmluZCBvdXQgbW9yZSBhYm91dCB0aGVzZSBnZW5lIHNldHMgb24gdGhlIFttc2lnZGJdKGh0dHA6Ly9zb2Z0d2FyZS5icm9hZGluc3RpdHV0ZS5vcmcvZ3NlYS9tc2lnZGIpIHdlYnNpdGUuIEhvd2V2ZXIsIGl0IGlzIGltcG9ydGFudCB0aGF0IHRoZXNlIGdlbmUgc2V0cyBoYXZlICoqSHVtYW4gZ2VuZSBzeW1ib2xzKiogb25seS4KCiMjIFByZXBhcmluZyB0aGUgZ2VuZSBzZXQgZm9yIGEgR1NFQSBhbmFseXNpcwoKRm9yIHRob3NlIGludGVyZXN0ZWQsIGhlcmUgaXMgdGhlIFIgY29kZSB0byBwcm9kdWNlIGEgZmlsZSBzdWl0YWJsZSBmb3IgR1NFQSBhbmFseXNpczotCgpgYGB7cn0KbGlicmFyeShkcGx5cikKZGVUYWJsZSA8LSByZWFkLmNzdigidDQ3ZF9UcmVhdG1lbnRfREVBX1Byb2ctdnMtQ29udHJvbF9hbGwuY3N2IikKZGVUYWJsZSAlPiUgYXJyYW5nZShkZXNjKHN0YXQpKSAlPiUgCiAgZHBseXI6OjpzZWxlY3Qoc3ltYm9sLCBzdGF0KSAlPiUgCiAgZmlsdGVyKCFpcy5uYShzeW1ib2wpKSAtPiBvcmRlcmVkVGFibGUKb3JkZXJlZFRhYmxlCmBgYAoKTm93IGxldHMgd2Fsa3Rocm91Z2ggaG93IHRvIGRvIHRoYXQgaW4gR2FsYXh5Oi0KCiMjIyBSYW5raW5nIHRoZSBnZW5lcwoKRnJvbSB0aGUgKmNsZWFuZWQqIHRhYmxlIGNyZWF0ZWQgYWJvdmUsIHNvcnQgb24gY29sdW1uIDUgKHRoZSB0ZXN0IHN0YXRpc3RpYykKCioqKkZpbHRlciBhbmQgU29ydCoqKiAtPiAqKipTb3J0IGRhdGEgaW4gYXNjZW5kaW5nIG9yIGRlc2NlbmRpbmcgb3JkZXIqKioKCiFbXShpbWFnZXMvZ2FsYXh5LTEwLnBuZykKCkdpdmluZyB0aGUgb3V0cHV0Oi0KCiFbXShpbWFnZXMvZ2FsYXh5LTExLnBuZykKCldlIG5vdyBoYXZlIGFsbCB0aGUgaW5mb3JtYXRpb24gd2UgbmVlZCwgYnV0IHRoZSBHU0VBIHRvb2wgZXhwZWN0cyBvbmx5IHR3byBjb2x1bW5zOyBnZW5lIHN5bWJvbCBhbmQgcmFua2VkIHN0YXRpc3RpYwoKIyMjIEN1dHRpbmcgdGhlIGNvbHVtbnMKCkV4dHJhY3QgY29sdW1ucyA4IChnZW5lIHN5bWJvbCkgZm9sbG93ZWQgYnkgdGVzdCBzdGF0aXN0aWM7IGBjOCxjNWAKCioqKlRleHQgTWFuaXB1bGF0aW9uKioqIC0+ICoqKkN1dCBjb2x1bW5zIGZyb20gYSB0YWJsZSoqKgoKIVtdKGltYWdlcy9nYWxheHktMTIucG5nKQoKCiMjIFJ1bm5pbmcgYSBHU0VBIGFuYWx5c2lzCgojIyMgVXNpbmcgdGhlIGZHU0VBIHRvb2wKCmBmR1NFQWAgaXMgYSBCaW9jb25kdWN0b3IgcGFja2FnZSB0aGF0IGltcGxlbWVudHMgdGhlIHByZS1yYW5rZWQgR1NFQSBhbmFseXNpcy4gSXQgYWxzbyBoYXBwZW5zIHRvIGJlIGEgZmFzdGVyIGltcGxlbWVudGF0aW9uLCBhcyBkZXNjcmliZWQgaW4gdGhlaXIgW3ByZS1wcmludF0oaHR0cDovL2Jpb3J4aXYub3JnL2NvbnRlbnQvZWFybHkvMjAxNi8wNi8yMC8wNjAwMTIpCgpZb3UgY2FuIGZpbmQgdGhlIHRvb2wgdW5kZXIgKioqTkdTOiBSTkEtc2VxIEFuYWx5c2lzKioqCgohW10oaW1hZ2VzL2dhbGF4eS0xMy5wbmcpCgpXaXRoIHRoaXMgdG9vbCwgdGhyZWUgZGlmZmVyZW50IG91dHB1dHMgYXJlIHByb2R1Y2VkCgotIGEgcmVzdWx0cyB0YWJsZQotIGEgc3VtbWFyeSBncmFwaGljIG9mIHRoZSB0b3AgcGF0aHdheXMKLSBlbnJpY2htZW50IHBsb3RzIGZvciB0aGUgdG9wIHBhdGh3YXlzCgojIyBUaGluZ3MgdG8gdHJ5OgoKLSBDYW4geW91IGlkZW50aWZ5IHBhdGh3YXlzIHRoYXQgYXJlIHNpZ25pZmljYW50IGF0IDAuMDUgbGV2ZWw/Ci0gV2hhdCBwYXRod2F5cyBhcmUgb3Zlci0gYW5kIHVuZGVyLXJlcHJlc2VudGVkPwo=